﻿// Copyright (c) Aura development team - Licensed under GNU GPL
// For more information, see license file in the main folder

using Aura.Channel.Network.Sending;
using Aura.Mabi;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Aura.Channel.World.Entities.Creatures
{
	/// <summary>
	/// Holds all conditions of a creature.
	/// </summary>
	/// <remarks>
	/// "Extra" values are information about conditions that are stored in
	/// MabiDictionaries, they appear after the actual conditions in the
	/// ConditionUpdate packet. An example of such a condition is ConditionsC.Hurry,
	/// which is used to modify your movement speed.
	/// To set those values, prepare a MabiDictionary before calling Activate
	/// and pass it as the optional "extra" value. They are removed
	/// automatically on deactivating.
	/// </remarks>
	/// <example>
	/// creature.Conditions.Activate(ConditionId.Petrified);
	/// 
	/// var extra = new MabiDictionary();
	/// extra.SetShort("VAL", speedBonus);
	/// creature.Conditions.Activate(ConditionId.Hurry, extra);
	/// </example>
	public class CreatureConditions
	{
		private Creature _creature;

		private Dictionary<int, CreatureCondition> _conditions = new Dictionary<int, CreatureCondition>();
		
		/// <summary>
		/// Raised when any conditions change.
		/// </summary>
		public event Action<Creature> Changed;

		public CreatureConditions(Creature creature)
		{
			_creature = creature;
		}

		public bool TryGetCondition(int conditionId, out CreatureCondition condition)
		{
			lock (_conditions)
				return _conditions.TryGetValue(conditionId, out condition);
		}

		public List<CreatureCondition> GetActiveConditions()
		{
			lock (_conditions)
				return _conditions.Values.ToList();
		}

		public bool Has(int condition)
		{
			lock (_conditions)
				return _conditions.ContainsKey(condition);
		}

		public void Activate(int conditionId, MabiDictionary extra = null, TimeSpan? duration = null)
		{
			lock (_conditions)
			{
				_conditions[conditionId] = new CreatureCondition(conditionId, extra, duration);
			}

			Send.ConditionUpdate(_creature, conditionId); 
			this.Changed.Raise(_creature); 
		}
		
		public void Deactivate(int conditionId)
		{
			lock (_conditions)
			{
				_conditions.Remove(conditionId);
			}

			Send.ConditionUpdate(_creature, conditionId);
			this.Changed.Raise(_creature);
		}

		/// <summary>
		/// Removes overdue conditions.
		/// </summary>
		/// <param name="time"></param>
		public void OnSecondsTimeTick(ErinnTime time)
		{
			IEnumerable<int> deactivate = null;

			lock (_conditions)
			{
				if (_conditions.Count == 0)
					return;

				deactivate = _conditions.Where(a => time.DateTime > a.Value.EndTime).Select(a => a.Key);
			}

			foreach (var conditionId in deactivate)
			{
				this.Deactivate(conditionId);
			}
		}

		/// <summary>
		/// Returns extra val for id, or 0.
		/// </summary>
		/// <param name="conditionId"></param>
		/// <returns></returns>
		public short GetExtraVal(int conditionId)
		{
			lock (_conditions)
			{
				if (!_conditions.TryGetValue(conditionId, out var condition))
					return 0;

				return condition.Extra.GetShort("VAL");
			}
		}

		/// <summary>
		/// Returns extra data for id, or null.
		/// </summary>
		/// <param name="conditionId"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public object GetExtraField(int conditionId, string name)
		{
			lock (_conditions)
			{
				if (!_conditions.TryGetValue(conditionId, out var condition))
					return null;

				return condition.Extra.Get(name);
			}
		}
	}

	public class CreatureCondition
	{
        public int Id { get; }
		public MabiDictionary Extra { get; }
		public TimeSpan? Duration { get; }
		public DateTime EndTime { get; }

        public CreatureCondition(int id, MabiDictionary extra = null, TimeSpan? duration = null)
        {
			this.Id = id;
			this.Extra = extra ?? new MabiDictionary();
			this.Duration = duration;

			if (duration != null)
				this.EndTime = DateTime.Now.Add((TimeSpan)duration);
			else
				this.EndTime = DateTime.MaxValue;
        }
	}
}
